package ch.epfl.lara.matcheck.types;



//trait Types  {
/*  val symbols: Symbols
  import symbols._
*/  
  
  // internal type
  sealed abstract class Type {
  
    def isSubtype(that: Type): Boolean = (this, that) match {
      case (IClassType(c1), IClassType(c2)) =>
        c1 isSubclassOf c2
        
      case _ => this.isSametype(that) 
    }

  
    def isSametype(that: Type): Boolean = (this, that) match {
      case (IBadType, _) => true

      case (_, IBadType) => true 

      case _ => this == that
    }

  
    def lub(that: Type): Option[Type] = (this, that) match {
      case (IClassType(c1), IClassType(c2)) =>
        intersection(c1.superclasses, c2.superclasses)

      case _ => 
        if (this isSubtype that) Some(that)
        else if (that isSubtype this) Some(this)
        else None
    }

    def intersection(xs: List[ClassSymbol], ys: List[ClassSymbol]): Option[Type] = xs.intersect(ys) match {
      case Nil => None
      case head :: tail => Some(IClassType(head))
    }
      
  }

  
  case class IClassType(c: ClassSymbol) extends Type {
    override def toString = c.name
  }

  case class IObjectType(o: ObjectSymbol) extends Type {
    override def toString = o.name
  }

  //Integer type
  case object IIntType extends Type {
    override def toString = "IType"
  }

  //Boolean type
  case object IBoolType extends Type {
    override def toString = "BType"
  }

  case object IStringType extends Type {
    override def toString = "<String>"
  }

  //Bad type
  case object IBadType extends Type {
    override def toString = "<Bad>"
  }

  
  //Built-in Option type
  case class IOptionType(c: List[Type]) extends Type {
   override def toString = "Option["+c.mkString("(",",",")")+"]"
  } 

  /**
  case class IListType(t: Type) extends Type {
    override def toString = "List[" + t + "]"
  }*/
  case class IParamType(tpe: Type,ptpe: Type) extends Type {
    override def toString = tpe+"of" + ptpe
  }
//}